package com.hh.game.service.gateway.filter.security;

import com.hh.game.common.base.manager.session.Session;
import com.hh.game.service.gateway.codec.Request;
import com.hh.game.service.gateway.codec.Response;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.util.Set;

@Slf4j
public abstract class SecurityFilter extends ChannelDuplexHandler {
    private static final AttributeKey<AuthorizingRealm> AUTHORIZING_REALM = AttributeKey.valueOf("GAME_AUTHORIZING_REALM");
    private SecurityConfig config;
    private SecurityManager manager;
    private boolean isLogin;

    public SecurityFilter(SecurityConfig config ,SecurityManager manager) {
        this.config = config == null ? new SecurityConfig() : config;
        if(manager == null){
            throw new IllegalArgumentException("Security Manager can not be null");
        }
        this.manager = manager;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof Request){
            Request request = (Request) msg;
            String route = request.getRoute();
            if(!config.isLoginRoute(route)){
                AuthorizingRealm realm = ctx.channel().attr(AUTHORIZING_REALM).get();
                SecurityEnum securityEnum = config.getSecurityEnum(route);
                if(realm != null){
                    if(!realm.isPermitted(route ,securityEnum)){
                        noticeClientAccessDenied(ctx);
                        return;
                    }
                }else if(securityEnum != SecurityEnum.NONE) {
                    noticeClientAccessDenied(ctx);
                    return;
                }

            }
        }
        super.channelRead(ctx, msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if(!isLogin && msg instanceof Response){
            try {
                Response response = (Response) msg;
                String route = response.getRoute();
                if (config.isLoginRoute(route)) {
                    if (this.manager.login(response)) {
                        this.isLogin = true;
                        Set<String> perms = this.manager.getPermission(response);
                        AuthorizingRealm realm = new AuthorizingRealm(perms);
                        Session session = ctx.channel().attr(Session.SESSION_KEY).get();
                        session.login();
                        realm.setSession(session);
                        ctx.channel().attr(AUTHORIZING_REALM).set(realm);
                    }
                }
            }catch (Exception e){
                log.error("security filter error" + e.getMessage());
            }
        }
        super.write(ctx, msg, promise);
    }

    protected abstract void noticeClientAccessDenied(ChannelHandlerContext ctx);

}
